{% extends "layout.html" %}
{% set active_page = "function" %}
{% set help_page = ["https://kizniche.github.io/Mycodo/Functions/", dict_translation['function']['title']] %}

{% block title %} - {{dict_translation['function']['title']}}{% endblock %}

{% block head %}
<link rel="stylesheet" type="text/css" href="/static/css/bootstrap-clockpicker.min.css">
<script type="text/javascript" src="/static/js/bootstrap-clockpicker.min.js"></script>

<link rel="stylesheet" href="/static/css/gridstack.css" />
<link rel="stylesheet" href="/static/css/gridstack-custom.css" />
<script src="/static/js/gridstack-all.js"></script>

<link href="/static/css/toastr.min.css" rel="stylesheet"/>
<script src="/static/js/toastr.min.js"></script>

<script type="text/javascript">
    $(document).ready(function () {
    toastr.options = {
      "closeButton": true,
      "debug": false,
      "newestOnTop": false,
      "progressBar": false,
      "positionClass": "toast-top-left",
      "preventDuplicates": false,
      "onclick": null,
      "showDuration": "300",
      "hideDuration": "1000",
      "timeOut": "15000",
      "extendedTimeOut": "10000",
      "showEasing": "swing",
      "hideEasing": "linear",
      "showMethod": "fadeIn",
      "hideMethod": "fadeOut"
    }
  });
</script>
{% endblock %}

{% block body %}
  <!-- Route: /function -->
  <main role="main" class="container">
    {% include 'flash_messages.html' %}

    <h4>{{dict_translation['function']['title']}} <a href="{{help_page[0]}}" target="_blank"><span style="font-size: 16px" class="fas fa-question-circle"></span></a></h4>
    <p>{{_('Functions conduct complex tasks, often coupling one or more <a href="/input">Inputs</a> with <a href="/output">Outputs</a>. For example, this could be using a PID Controller to create a feedback loop with a temperature sensor and heater in order to regulate temperature.')}}</p>

    <div style="clear: both; padding: 0.5em 0;"></div>

    <form id="new_function_form" method="post" action="/function">
    {{form_mod_pid_base.csrf_token}}

    <div class="row small-gutters" style="padding-left: 0.75em">
      <div class="col-auto">
        <select class="selectpicker" data-live-search="true" title="{{_('Function')}}: {{dict_translation['select_one']['title']}}" id="function_type" name="function_type">
        {% for each_controller in choices_functions_add -%}
          <option value="{{each_controller['value']}}">{{each_controller['item']}}</option>
        {% endfor -%}
        </select>
      </div>

      <div class="col-auto">
        <input onclick="return $(this).processRequest(this, 'function_add');" name="function_add" value="{{_('Add')}}" class="btn btn-primary" type="button"/>
      </div>
    </div>

    </form>

    <div style="clear: both; padding: 1em 0;"></div>

    <div class="grid-stack">
      {%- for each_function in conditional -%}
        {% set controller_type = "Conditional" %}
      <div id="gridstack_function_{{each_function.unique_id}}"
        class="grid-stack-item"
        gs-id="{{each_function.unique_id}}"
        gs-x="0"
        gs-y="{{each_function.position_y}}"
        gs-w="24"
        gs-h="1"
        gs-no-resize="true">
        {% include 'pages/function_options/conditional_entry.html' %}
      </div> <!-- grid-stack-item -->
      {% endfor %}

      {%- for each_function in pid -%}
      <div id="gridstack_function_{{each_function.unique_id}}"
        class="grid-stack-item"
        gs-id="{{each_function.unique_id}}"
        gs-x="0"
        gs-y="{{each_function.position_y}}"
        gs-w="24"
        gs-h="1"
        gs-no-resize="true">
        {% include 'pages/function_options/pid_entry.html' %}
      </div> <!-- grid-stack-item -->
      {% endfor %}

      {%- for each_function in trigger -%}
      <div id="gridstack_function_{{each_function.unique_id}}"
        class="grid-stack-item"
        gs-id="{{each_function.unique_id}}"
        gs-x="0"
        gs-y="{{each_function.position_y}}"
        gs-w="24"
        gs-h="1"
        gs-no-resize="true">
        {% include 'pages/function_options/trigger_entry.html' %}
      </div> <!-- grid-stack-item -->
      {% endfor %}

      {%- for each_function in function_dev -%}
      <div id="gridstack_function_{{each_function.unique_id}}"
        class="grid-stack-item"
        gs-id="{{each_function.unique_id}}"
        gs-x="0"
        gs-y="{{each_function.position_y}}"
        gs-w="24"
        gs-h="1"
        gs-no-resize="true">
        {% include 'pages/function_options/function_entry.html' %}
      </div> <!-- grid-stack-item -->
      {% endfor %}

      {%- for each_function in function -%}
      <div id="gridstack_function_{{each_function.unique_id}}"
        class="grid-stack-item"
        gs-id="{{each_function.unique_id}}"
        gs-x="0"
        gs-y="{{each_function.position_y}}"
        gs-w="24"
        gs-h="1"
        gs-no-resize="true">
        {% include 'pages/function_options/custom_function_entry.html' %}
      </div> <!-- grid-stack-item -->
      {% endfor %}
    </div> <!-- grid-stack -->

  {% include 'admin/dependencies_install.html' %}

  </main>

  <div style="clear: both; padding-bottom: 1.5em;"></div>

  <script type="text/javascript">
    let gridstack_item_height = $(".grid-stack-item-content").eq(0).outerHeight(true);

    // Grid
    let options = {
      cellHeight: gridstack_item_height + 5,
      column: 24,
      margin: 0,
      draggable: {
        handle: '.panel-heading'
      },
      float: false
    };
    let grid = GridStack.init(options);

    grid.on('change', function (event, items) {
      let gridstack_item_height = $(".grid-stack-item-content").eq(0).outerHeight(true);
      if (gridstack_item_height) {
        grid.cellHeight(gridstack_item_height + 5);
      }
      let serializedFull = grid.save(false);
      $.ajax({
        type: "POST",
        url: "/save_function_layout",
        headers: {"X-CSRFToken": "{{form_function_base.csrf_token._value()}}"},
        data: JSON.stringify(serializedFull, null, '  '),
        contentType: "application/json; charset=utf-8",
        success: function (data) {}
      });
    });

    function popup_response(data) {
      if ('error' in data.data.messages && data.data.messages.error.length !== 0) {
        toastr['error']('Error: ' + data.data.messages.error.join(", "));
      }
      {% if not misc.hide_alert_warning %}
      if ('warning' in data.data.messages && data.data.messages.warning.length !== 0) {
        toastr['warning']('Warning: ' + data.data.messages.warning.join(", "));
      }
      {% endif %}
      {% if not misc.hide_alert_info %}
      if ('info' in data.data.messages && data.data.messages.info.length !== 0) {
        toastr['info']('Info: ' + data.data.messages.info.join(", "));
      }
      {% endif %}
      {% if not misc.hide_alert_success %}
      if ('success' in data.data.messages && data.data.messages.success.length !== 0) {
        toastr['success']('Success: ' + data.data.messages.success.join(", "));
      }
      {% endif %}
    }

    var status_message = {};

    function function_status(function_id) {
      const url = '/function_status_activated/' + function_id;
      $.getJSON(url,
        function(data, responseText, jqXHR) {
          if (jqXHR.status !== 204) {
            if (data && "string_status" in data) {
              document.getElementById("function_status_activated_" + function_id).innerHTML = data['string_status'].replace(/(?:\r\n|\r|\n)/g, "<br>");
            } else {
              document.getElementById("function_status_activated_" + function_id).innerHTML = '';
            }
            if (data && "error" in data && data['error'].length) {
              let errors = ''
              for (let c = 0; c < data['error'].length; c++) {
                errors += data['error'][c].replace(/(?:\r\n|\r|\n)/g, "<br>") + '<br>';
              }
              document.getElementById("function_status_activated_" + function_id).innerHTML += "<p><pre>Error(s):<br>" + errors + "</pre></p>";
            }
          }
          else {
            document.getElementById("function_status_activated_" + function_id).innerHTML = "Error querying status.";
          }
        }
      );
    }
    // Repeat function for function_status()
    function repeat_function_status(function_id, period_sec) {
      status_message[function_id] = setInterval(function () {
        function_status(function_id)
      }, period_sec * 1000);
    }

    function function_status_always(function_id) {
      const url_2 = '/function_status_always/' + function_id;
      $.getJSON(url_2,
        function(data, responseText, jqXHR) {
          if (jqXHR.status !== 204) {
            if (data && "string_status" in data) {
              document.getElementById("function_status_always" + function_id).innerHTML = data['string_status'].replace(/(?:\r\n|\r|\n)/g, "<br>");
            } else {
              document.getElementById("function_status_always" + function_id).innerHTML = '';
            }
            if (data && "error" in data && data['error'].length) {
              let errors = ''
              for (let c = 0; c < data['error'].length; c++) {
                errors += data['error'][c].replace(/(?:\r\n|\r|\n)/g, "<br>") + '<br>';
              }
              document.getElementById("function_status_always" + function_id).innerHTML += "<p><pre>Error(s):<br>" + errors + "</pre></p>";
            }
          }
          else {
            document.getElementById("function_status_always" + function_id).innerHTML = "Error querying status.";
          }
        }
      );
    }
    // Repeat function for function_status_always()
    function repeat_function_status_always(function_id, period_sec) {
      status_message[function_id] = setInterval(function () {
        function_status_always(function_id)
      }, period_sec * 1000);
    }

    jQuery(document).ready(function($) {
    {%- for each_function in function if 'period_status' in custom_options_values_controllers[each_function.unique_id] %}
      function_status_always('{{each_function.unique_id}}');
      repeat_function_status_always('{{each_function.unique_id}}', {{custom_options_values_controllers[each_function.unique_id]['period_status']}});
      {%- if each_function.is_activated -%}
      function_status('{{each_function.unique_id}}');
      repeat_function_status('{{each_function.unique_id}}', {{custom_options_values_controllers[each_function.unique_id]['period_status']}});
      {%- endif -%}
    {% endfor %}
    {%- for each_conditional in conditional if each_conditional.is_activated %}
      function_status('{{each_conditional.unique_id}}');
      repeat_function_status('{{each_conditional.unique_id}}', 30);
    {% endfor %}
    {%- for each_pid in pid if each_pid.is_activated %}
      function_status('{{each_pid.unique_id}}');
      repeat_function_status('{{each_pid.unique_id}}', 30);
    {% endfor %}
    });

    function function_status_reset(function_id) {
      $('#function_status_' + function_id)
        .removeClass("inactive-background")
        .removeClass("active-background")
        .removeClass("hold-background")
        .removeClass("pause-background");
    }
    function ids_hide(pid_id, class_list) {
      for (let c = 0; c < class_list.length; c++) {
        let id_str = class_list[c] + "_" + pid_id;
        if (document.getElementById(id_str) != null) {
          document.getElementById(id_str).style.display = "none";
        }
      }
    }
    function ids_show(pid_id, class_list) {
      for (let c = 0; c < class_list.length; c++) {
        let id_str = class_list[c] + "_" + pid_id;
        if (document.getElementById(id_str) != null) {
          document.getElementById(id_str).style.display = "";
        }
      }
    }

    function getFormData(form) {
      let rawJson = form.serializeArray();
      let model = {}; $.map(rawJson, function (n, i) {
        model[n['name']] = n['value'];
      });
      return model;
    }

    jQuery.fn.processRequest = function(button, action) {
      console.log("Action: ", action)
      let form_values = getFormData($(button).parents('form'));
      if (action === 'function_add' ||
          action === 'function_mod' ||
          action === 'function_delete' ||
          action === 'function_activate' ||
          action === 'function_deactivate' ||
          action === 'execute_all_actions' ||
          action === 'add_action' ||
          action === 'save_action' ||
          action === 'delete_action' ||
          action === 'add_condition' ||
          action === 'save_condition' ||
          action === 'delete_condition' ||
          action === 'pid_pause' ||
          action === 'pid_hold' ||
          action === 'pid_resume' ||
          action.startsWith('custom_button_')) {
        {% if not misc.hide_alert_info %}toastr['info']('Command sent. Please wait...');{% endif %}
        $.ajax({
          type: "POST",
          url: '/function_submit',
          headers: {"X-CSRFToken": "{{form_function_base.csrf_token._value()}}"},
          data: $(button).parents('form').serialize() + '&' + action + '=1',
          success: function (data) {
            if (action === 'function_add' && 'function_id' in data.data) {
              if ('dep_unmet' in data.data && data.data.dep_unmet.length > 0) {
                $(".dependencies_device_name").text(data.data.dep_name);
                $(".dependencies_device").text(data.data.dep_unmet);
                $(".dependencies_unmet").text(data.data.dep_list.join(", "));
                document.getElementById("dependency_unmet").value = data.data.dep_unmet;
                if (data.data.dep_message.length > 0) {
                  document.getElementById("dependencies_message").innerHTML = "<p>Message about dependencies: " + data.data.dep_message + "</p>";
                }
                $('#modal_config_dependency_install').modal('show');
                popup_response(data);
              } else {
                $.ajax({
                  type: "GET",
                  url: '/function?function_type=entry&function_id=' + data.data.function_id,
                  success: function (new_entry) {
                    if (data.data.messages.success.length !== 0) {
                      grid.addWidget('<div class="grid-stack-item" id="gridstack_function_' + data.data.function_id + '" gs-id="' + data.data.function_id + '">' + new_entry + '</div>', {
                        w: 24,
                        h: 1,
                        y: 999
                      });
                      {% if not hide_tooltips %}$('input[title]').tooltip({placement: 'top'});{% endif %}
                      $('#action_type_' + data.data.function_id).selectpicker('refresh');
                      $('#condition_type_' + data.data.function_id).selectpicker('refresh');
                    }
                    popup_response(data);
                  },
                  error: function() {
                    toastr['error']('Error: Could not get new Function entry');
                  }
                });
              }
              return;
            }
            else if (action === 'add_action' && 'dep_unmet' in data.data && data.data.dep_unmet.length > 0) {
              $(".dependencies_device_name").text(data.data.dep_name);
              $(".dependencies_device").text(data.data.dep_unmet);
              $(".dependencies_unmet").text(data.data.dep_list.join(", "));
              document.getElementById("dependency_unmet").value = data.data.dep_unmet;
              if (data.data.dep_message.length > 0) {
                document.getElementById("dependencies_message").innerHTML = "<p>Message about dependencies: " + data.data.dep_message + "</p>";
              }
              $('#modal_config_dependency_install').modal('show');
            }

            if (data.data.messages.error.length === 0) {
              if (action === 'function_mod' && 'function_id' in data.data) {
                if ('return_text' in data.data.messages && data.data.messages.return_text.length > 0) {
                  document.getElementById("return_text_" + data.data.function_id).innerHTML = data.data.messages.return_text.join("<br/><br/>");
                }
                if ('name' in data.data.messages &&
                    document.getElementById("function_name_" + data.data.function_id)) {
                  if (document.getElementById("function_name_" + data.data.function_id).tagName === "DIV") {
                    document.getElementById("function_name_" + data.data.function_id).innerHTML = data.data.messages.name;
                  } else if (document.getElementById("function_name_" + data.data.function_id).tagName === "INPUT") {
                    document.getElementById("function_name_" + data.data.function_id).value = data.data.messages.name;
                  }
                }
              }
              else if (action === 'function_delete' && 'function_id' in data.data) {
                $('#modal_config_' + data.data.function_id).modal('hide');
                $('.modal-backdrop').remove();
                $(document.body).removeClass("modal-open");
                grid.removeWidget('#gridstack_function_' + data.data.function_id);
              }
              else if (action === 'function_activate' && 'function_id' in data.data) {
                function_status_reset(data.data.function_id);
                $('#function_status_' + data.data.function_id).addClass("active-background");
                if (document.getElementById('function_activate_' + data.data.function_id) != null) {
                  document.getElementById('function_activate_' + data.data.function_id).style.display = "none";
                }
                if (document.getElementById('function_deactivate_' + data.data.function_id) != null) {
                  document.getElementById('function_deactivate_' + data.data.function_id).style.display = "";
                }
                if (document.getElementById('form_pid_' + data.data.function_id) != null) {
                  ids_hide(data.data.function_id, ["pid_activate", "pid_resume"]);
                  ids_show(data.data.function_id, ["pid_deactivate", "pid_pause", "pid_hold"]);
                  document.getElementById("pid_activity_indicator_" + data.data.function_id).value = "PID Controller [Active]";
                }
                if ("function_type" in form_values && ["conditional", "function", "pid"].includes(form_values.function_type)) {
                  ids_show(data.data.function_id, ["status_activated_enable"]);
                  function_status(data.data.function_id);
                  if ("period_status" in data.data.messages && data.data.messages.period_status) {
                    repeat_function_status(data.data.function_id, data.data.messages.period_status);
                  } else {
                    repeat_function_status(data.data.function_id, 30);
                  }
                }
              }
              else if (action === 'function_deactivate' && 'function_id' in data.data) {
                function_status_reset(data.data.function_id);
                $('#function_status_' + data.data.function_id).addClass("inactive-background");
                if (document.getElementById('function_activate_' + data.data.function_id) != null) {
                  document.getElementById('function_activate_' + data.data.function_id).style.display = "";
                }
                if (document.getElementById('function_deactivate_' + data.data.function_id) != null) {
                  document.getElementById('function_deactivate_' + data.data.function_id).style.display = "none";
                }
                if (document.getElementById('form_pid_' + data.data.function_id) != null) {
                  ids_hide(data.data.function_id, ["pid_deactivate", "pid_pause", "pid_hold", "pid_resume"]);
                  ids_show(data.data.function_id, ["pid_activate"]);
                  document.getElementById("pid_activity_indicator_" + data.data.function_id).value = "PID Controller [Inactive]";
                  document.getElementById("function_status_activated_" + data.data.function_id).innerHTML = "";
                }
                if ("function_type" in form_values && ["conditional", "function", "pid"].includes(form_values.function_type)) {
                  ids_hide(data.data.function_id, ["status_activated_enable"]);
                  if (data.data.function_id in status_message) {
                    clearInterval(status_message[data.data.function_id]);
                    delete status_message[data.data.function_id];
                  }
                }
              }
              else if (action === 'pid_pause' && 'function_id' in data.data) {
                function_status_reset(data.data.function_id);
                $('#function_status_' + data.data.function_id).addClass("pause-background");
                ids_hide(data.data.function_id, ["pid_activate", "pid_pause"]);
                ids_show(data.data.function_id, ["pid_deactivate", "pid_hold", "pid_resume"]);
                document.getElementById("pid_activity_indicator_" + data.data.function_id).value = "PID Controller [Paused]";
              }
              else if (action === 'pid_hold' && 'function_id' in data.data) {
                function_status_reset(data.data.function_id);
                $('#function_status_' + data.data.function_id).addClass("hold-background");
                ids_hide(data.data.function_id, ["pid_aactivate", "pid_hold"]);
                ids_show(data.data.function_id, ["pid_deactivate", "pid_pause", "pid_resume"]);
                document.getElementById("pid_activity_indicator_" + data.data.function_id).value = "PID Controller [Held]";
              }
              else if (action === 'pid_resume' && 'function_id' in data.data) {
                function_status_reset(data.data.function_id);
                $('#function_status_' + data.data.function_id).addClass("active-background");
                ids_hide(data.data.function_id, ["pid_activated", "pid_resume"]);
                ids_show(data.data.function_id, ["pid_deactivate", "pid_pause", "pid_hold"]);
                document.getElementById("pid_activity_indicator_" + data.data.function_id).value = "PID Controller [Active]";
              }
              if (action === 'delete_action' && 'action_id' in data.data) {
                document.getElementById("mod_action_" + data.data.action_id).remove();
                popup_response(data);
              }
              if (action === 'delete_condition' && 'condition_id' in data.data) {
                document.getElementById("mod_function_condition_" + data.data.condition_id).remove();
                popup_response(data);
              }
              if (action === 'function_mod' && 'page_refresh' in data.data && data.data.page_refresh) {
                $.ajax({
                  type: "GET",
                  url: '/function?function_type=options&function_id=' + data.data.function_id,
                  success: function (new_options) {
                    $('#mod_function_' + data.data.function_id).html(new_options);
                    popup_response(data);
                  },
                  error: function() {
                    toastr['error']('Error: Could not get new Function options');
                  }
                });
              }
              else if (action === 'add_action' && 'page_refresh' in data.data && data.data.page_refresh) {
                $.ajax({
                  type: "GET",
                  url: '/function?function_type=actions&function_id=' + data.data.function_id + '&action_id=' + data.data.action_id,
                  success: function (new_options) {
                    document.getElementById('mod_function_actions_' + data.data.function_id).insertAdjacentHTML('beforeend', (new_options));
                    popup_response(data);
                  },
                  error: function() {
                    toastr['error']('Error: Could not get Function Actions');
                  }
                });
              }
              else if (action === 'add_condition' && 'page_refresh' in data.data && data.data.page_refresh) {
                $.ajax({
                  type: "GET",
                  url: '/function?function_type=conditions&function_id=' + data.data.function_id + '&condition_id=' + data.data.condition_id,
                  success: function (new_options) {
                    document.getElementById('mod_function_conditions_' + data.data.function_id).insertAdjacentHTML('beforeend', (new_options));
                    popup_response(data);
                  },
                  error: function() {
                    toastr['error']('Error: Could not get Function Conditions');
                  }
                });
              } else {
                popup_response(data);
              }
            } else {
              popup_response(data);
            }
          },
          error: function() {
            toastr['error']('Error: Could not communicate with server');
          }
        });
      }
    }

    $('.clockpicker').clockpicker({
      donetext: 'Done'
    });

    $('.collapse').on('show.bs.collapse', function(){
      $(this).parent().find(".fa-plus-square").removeClass("fa-plus-square").addClass("fa-minus-square");
    }).on('hide.bs.collapse', function(){
      $(this).parent().find(".fa-minus-square").removeClass("fa-minus-square").addClass("fa-plus-square");
    });
  </script>

{% endblock %}
